#pragma once
#include "Err.hpp"
#include "Sock.hpp"
#include "Log.hpp"
#include "epoller.hpp"
#include <iostream>
#include <functional>
#include <cassert>
#include <unordered_map>

const uint16_t DEFAULT_PORT = 8888;

using func_t = std::function<std::string(std::string)>;

struct Connection;
using io_t = std::function<void(Connection *)>;
struct Connection
{

    /// IO信息
    int _fd;
    std::string _inbuffer; // 每个连接都应该有自己的收、发缓冲区
    std::string _outbuffer;
    /// IO处理函数
    io_t _recver;
    io_t _sender;
    io_t _excpter;
    // 客户端信息
    uint16_t _cilentPort;
    std::string _cilentIP;

    Connection(const int &fd, const std::string &clientip, const uint16_t &clientport)
        : _fd(fd), _cilentIP(clientip), _cilentPort(clientport)
    {
    }
    /// @brief 注册IO处理函数
    void RegisteIO(io_t recver, io_t sender, io_t excpter)
    {
        _recver = recver;
        _sender = sender;
        _excpter = excpter;
        logMessage(Debug, "注册IO函数成功");
    }
}; // END OF Connection

class EpollServer
{
    const static int N = 128;

public:
    EpollServer(func_t func, uint16_t port = DEFAULT_PORT)
        : _port(port)
    {
    }
    ~EpollServer()
    {
        _listen_sock.Close();
        _epoller.Close();
    }
    void Init()
    {
        logMessage(Debug, "Epoll服务器，启动！");
        _listen_sock.Socket();
        _listen_sock.Bind(_port);
        _listen_sock.Listen();

        // 获取一个epoll的句柄
        _epoller.Create();

        // 将监听套接字连接信息添加进_connections中
        AddConnection(_listen_sock.Fd(), EPOLLIN);

        logMessage(Debug, "Epoll服务器，初始化成功");
    }
    /// @brief 将连接信息，填写进_connections中，并让epoll监听
    void AddConnection(int fd, uint32_t events, std::string ip = "127.0.0.1", uint16_t port = DEFAULT_PORT)
    {
        /// 1.构建Connection对象
        Connection *conn = new Connection(fd, ip, port);
        /// 2.注册当前Connection的IO处理函数
        if (fd == _listen_sock.Fd())
            conn->RegisteIO(std::bind(&EpollServer::Acceptor, this, std::placeholders::_1), nullptr, nullptr);
        else
            conn->RegisteIO(std::bind(&EpollServer::Recver, this, std::placeholders::_1),
                            std::bind(&EpollServer::Sender, this, std::placeholders::_1),
                            std::bind(&EpollServer::Excpter, this, std::placeholders::_1));
        /// 3.写进_connections中，把Connection对象管理起来
        _connections.insert({fd, conn});
        /// 3.写进epoll，让内核帮我们进行监听事件
        bool r = _epoller.AddEvent(fd, events);
        assert(r);
        (void)r;
        logMessage(Debug, "AddConnection success, fd: %d, clientinfo: [%s:%d]", fd, ip.c_str(), port);
    }
    void Start()
    {
        int timeout = -1;
        while (true)
        {
            LoopOnce(timeout);
        }
    }
    void LoopOnce(int timeout)
    {
        /// 1.获取就绪的事件
        int n = _epoller.Wait(_revs, N, timeout);
        /// 2.为就绪的事件进行调用IO处理函数
        for (int i = 0; i < n; ++i)
        {
            int fd = _revs[i].data.fd; //?
            uint32_t events = _revs[i].events;
            logMessage(Debug, "当前正在处理%d上的%s", fd, (events & EPOLLIN) ? "EPOLLIN" : "OTHER");
            if (!ConnIsExists(fd))
                logMessage(Debug, "fd:%d 在_connections中找不到对应的Connection对象", fd);
            if ((events & EPOLLIN) && ConnIsExists(fd))
                _connections[fd]->_recver(_connections[fd]);
            if ((events & EPOLLOUT) && ConnIsExists(fd))
                _connections[fd]->_sender(_connections[fd]);
            if ((events & EPOLLERR) || (events & EPOLLHUP))
                _connections[fd]->_excpter(_connections[fd]);
        }
    }

    /// @brief 处理监听套接字的读事件
    void Acceptor(Connection *conn)
    {
        logMessage(Debug, "Here");
        std::string clientip;
        uint16_t clientport;
        // 此时Accept不会阻塞，因为listensocket已经读就绪了
        int sock = _listen_sock.Accept(&clientip, &clientport);
        if (sock < 0)
            return;
        logMessage(Debug, "%s:%d 已经连上了服务器了", clientip.c_str(), clientport);

        // 让epoll等待IO套接字的读事件
        AddConnection(sock, EPOLLIN, clientip, clientport);
    }
    /// @brief 处理IO套接字的读事件
    void Recver(Connection *conn)
    {
        char request[1024];
        // 此时recv不会阻塞，因为IO套接字已经读就绪了
        ssize_t s = recv(conn->_fd, request, sizeof(request) - 1, 0);
        if (s > 0)
        {
            request[s] = 0;
            std::string response = "[echo]";
            response += request;
            send(conn->_fd, response.c_str(), response.size(), 0);
        }
        else
        {
            if (s == 0)
                logMessage(Info, "client quit ...");
            else
                logMessage(Warning, "recv error, client quit ...");
            // 客户端已经退出或者recv读取失败，应该关闭fd以及删除epoll的相关fd
            // 让epoll不再关心该文件描述符的IO事件
            _epoller.DelEvent(conn->_fd);
            // 关闭该文件描述符
            close(conn->_fd);
        }
    }
    /// @brief 处理IO套接字的写事件
    void Sender(Connection *conn)
    {
    }
    /// @brief 处理套接字的异常事件
    void Excpter(Connection *conn)
    {
    }
    bool ConnIsExists(int fd)
    {
        return _connections.find(fd) != _connections.end();
    }

private:
    uint16_t _port;
    Sock _listen_sock;
    Epoller _epoller;
    struct epoll_event _revs[N];
    std::unordered_map<int, Connection *> _connections; // 保存服务器连接数据
};